package org.apache.james.hibernate;

import java.util.Date;

import javax.mail.Flags;
import javax.mail.internet.MimeMessage;

import org.apache.james.hibernate.entity.DbMailbox;
import org.apache.james.mailboxmanager.GeneralMessageSet;
import org.apache.james.mailboxmanager.MailboxListener;
import org.apache.james.mailboxmanager.MailboxManagerException;
import org.apache.james.mailboxmanager.MessageResult;
import org.apache.james.mailboxmanager.Quota;
import org.apache.james.mailboxmanager.acl.MailboxRights;
import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
import org.apache.james.mailboxmanager.impl.MailboxEventDispatcher;
import org.apache.james.mailboxmanager.impl.MessageResultImpl;
import org.apache.james.mailboxmanager.mailbox.AbstractGeneralMailbox;
import org.apache.james.mailboxmanager.mailbox.ImapMailboxSession;
import org.apache.james.mailboxmanager.tracking.UidChangeTracker;
import org.apache.james.mailboxmanager.util.UidToKeyConverter;
import org.apache.james.mailboxmanager.util.UidToKeyConverterImpl;
import org.apache.james.mailboxmanager.wrapper.UidToMsnBidiMap;
import org.apache.log4j.Logger;

public abstract class AbstractImapMailboxSession extends AbstractGeneralMailbox implements
		ImapMailboxSession {

	private final static Logger LOG = Logger.getLogger(AbstractImapMailboxSession.class);

	UidChangeTracker tracker;
	Flags permanentFlags;

	private MailboxEventDispatcher eventDispatcher = new MailboxEventDispatcher();

	private boolean open = true;

	private UidToKeyConverterImpl uidToKeyConverter;

	private UidToMsnBidiMap numberCache;

	public AbstractImapMailboxSession(DbMailbox mailboxRow, UidChangeTracker tracker) {
		this.tracker = tracker;
		tracker.addMailboxListener(getEventDispatcher());
		getUidToKeyConverter().setUidValidity(mailboxRow.getUidValidity());
	}

	@Override
	public Quota[] getQuota() {

		LOG.error("getQuota(" + getName() + ")");
		return null;
	}

	@Override
	public boolean isSelectable() {
		LOG.info("isSelectable (" + getName() + ")");
		return true;
	}

	@Override
	public MailboxRights myRights() {
		LOG.info("myRights (" + getName() + "):");
		return null;
	}

	@Override
	public MessageResult appendMessage(MimeMessage message, Date internalDate,
			int result) throws MailboxManagerException {
		LOG.info("appendMessage[" + getName()
				+ "] (message, internalDate, result):");
		return null;
	}

	@Override
	public int getMessageResultTypes() {
		LOG.info("getMessageResultTypes (" + getName() + "):");
		return MessageResult.FLAGS + MessageResult.INTERNAL_DATE
				+ MessageResult.KEY + MessageResult.MIME_MESSAGE
				+ MessageResult.SIZE + MessageResult.UID;
	}

	@Override
	public int getMessageSetTypes() {
		LOG.info("getMessageSetTypes (" + getName() + "):");
		return GeneralMessageSet.TYPE_ALL + GeneralMessageSet.TYPE_KEY
				+ GeneralMessageSet.TYPE_UID + GeneralMessageSet.TYPE_MESSAGE;
	}

	protected static int noMsnResult(int result) {
		if ((result & MessageResult.MSN) > 0) {
			result |= MessageResult.UID;
			result -= MessageResult.MSN;
		}
		return result;
	}


	@Override
	public String getName() {
		return null;
	}


	@Override
	public synchronized Flags getPermanentFlags() {
		if (permanentFlags == null) {
			permanentFlags = new Flags();
			permanentFlags.add(Flags.Flag.ANSWERED);
			permanentFlags.add(Flags.Flag.DELETED);
			permanentFlags.add(Flags.Flag.DRAFT);
			permanentFlags.add(Flags.Flag.FLAGGED);
			permanentFlags.add(Flags.Flag.RECENT);
			permanentFlags.add(Flags.Flag.SEEN);
		}
		return permanentFlags;
	}


	@Override
	public void added(MessageResult result) {
		// TODO Auto-generated method stub

	}

	@Override
	public void expunged(MessageResult mr) {
		// TODO Auto-generated method stub

	}

	@Override
	public void flagsUpdated(MessageResult result,
			MailboxListener silentListener) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mailboxDeleted() {
		// TODO Auto-generated method stub

	}

	@Override
	public void mailboxRenamed(String origName, String newName) {
		// TODO Auto-generated method stub

	}

	@Override
	public void close() throws MailboxManagerException {

		this.open = false;
	}

	@Override
	public boolean isWriteable() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public MessageResult[] getExpungedEvents(boolean reset)
			throws MailboxManagerException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public MessageResult[] getFlagEvents(boolean reset)
			throws MailboxManagerException {
		// TODO Auto-generated method stub
		return null;
	}

	protected MailboxEventDispatcher getEventDispatcher() {
		if (eventDispatcher == null) {
			eventDispatcher = new MailboxEventDispatcher();
		}
		return eventDispatcher;
	}

	protected UidChangeTracker getUidChangeTracker() {
		return tracker;
	}

	private void checkAccess() throws MailboxManagerException {
		if (!open) {
			throw new RuntimeException("mailbox is closed");
		} else if (getEventDispatcher().size() == 0) {
			throw new RuntimeException("mailbox has not been opened");
		} else if (getUidChangeTracker().isExisting()) {
			throw new MailboxManagerException("Mailbox is not existing");
		}
	}
	
    protected UidToMsnBidiMap getNumberCache() throws MailboxManagerException {
        if (numberCache == null) {
            MessageResult[] mr = getMessages(GeneralMessageSetImpl
                    .all(), MessageResult.UID);
            numberCache = new UidToMsnBidiMap();
            for (int i = 0; i < mr.length; i++) {
                numberCache.add(mr[i].getUid());
            }
        }
        return numberCache;
    }


	@Override
	public void addListener(MailboxListener listener, int result)
			throws MailboxManagerException {
		getEventDispatcher().addMailboxListener(listener);
		checkAccess();
	}

	@Override
	public void removeListener(MailboxListener mailboxListener) {
		if (!open || getEventDispatcher().size() == 0) {
			throw new RuntimeException("mailbox not open");
		}
		getEventDispatcher().removeMailboxListener(mailboxListener);
		if (getEventDispatcher().size() == 0) {
			open = false;
			getUidChangeTracker().removeMailboxListener(getEventDispatcher());
		}
	}

	protected UidToKeyConverter getUidToKeyConverter() {
		if (uidToKeyConverter == null) {
			uidToKeyConverter = new UidToKeyConverterImpl();
		}
		return uidToKeyConverter;
	}

	protected MessageResult[] addMsnToResults(MessageResult[] mr, int result)
			throws MailboxManagerException {
		MessageResult[] translated = new MessageResult[mr.length];
		for (int i = 0; i < translated.length; i++) {
			translated[i] = addMsnResult(mr[i], result);
		}
		return translated;
	}

	protected MessageResult addMsnResult(MessageResult mr, int result)
			throws MailboxManagerException {
		if (mr != null) {
			if ((result & MessageResult.MSN) > 0) {
				// TODO copy the MessageResult because it could be another class
				int msn = getNumberCache().getMsn(mr.getUid());
				((MessageResultImpl) mr).setMsn(msn);
			}
		}
		return mr;
	}

}
